home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / ccmd / ccmdut.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-04-13  |  16.5 KB  |  565 lines

  1. /*
  2.  Author: Andrew Lowry
  3.  
  4.  Columbia University Center for Computing Activities, July 1986.
  5.  Copyright (C) 1986, 1987, Trustees of Columbia University in the City
  6.  of New York.  Permission is granted to any individual or institution
  7.  to use, copy, or redistribute this software so long as it is not sold
  8.  for profit, provided this copyright notice is retained.
  9. */
  10. /* ccmdut
  11. **
  12. ** Utility routines for use by action routines, function handlers,
  13. ** etc.
  14. **/
  15.  
  16. #include "ccmdlib.h"            /* get ccmd package symbols */
  17. #include "cmfncs.h"            /* and internal symbols */
  18.  
  19. /* break table that breaks on everything, shared by some of the parse
  20. ** functions.
  21. **/
  22.  
  23. brktab cmallbk = {
  24.   {
  25.     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  26.     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
  27.   },
  28.   {
  29.     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  30.     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
  31.   }
  32. };
  33.         
  34.  
  35.  
  36. /* cmstin, cmsti1, cmsti  -- simulate terminal input
  37. **
  38. ** Purpose:
  39. **   Stuffs characters into the command line buffer, optionally
  40. **   echoing them as they are stuffed.  Use the three routines
  41. **   as follows:
  42. **
  43. **     ret = cmstin(s,n,flags); -- stuff n characters from s
  44. **     ret = cmsti1(c,flags);   -- stuff single character c
  45. **     ret = cmsti(s,flags);    -- stuff null-terminated string s
  46. **
  47. **   Characters are combined with the flags value before stuffing
  48. **   into the command line.  If the CC_NEC flag is on, no echoing
  49. **   occurs.
  50. **
  51. ** Input arguments:
  52. **   c - The character to be stuffed (cmsti1 only).
  53. **   s - A pointer to the string to be stuffed (cmstin and cmsti only).
  54. **   n - The number of characters to be stuffed (cmstin only).  -1 means
  55. **     stuff until a null character is encountered.
  56. **   flags - Flag bits to be set in the left half of the (int) entry
  57. **     for the character(s).  The right half of flags should be zero.
  58. **
  59. ** Output arguments: None.
  60. ** Returns: Standard error code
  61. **/
  62.  
  63. int
  64. cmstin(s,n,flags)
  65. char *s;
  66. int n, flags;
  67. {
  68.   char c;            /* individual chars to fill */
  69.  
  70.   if ((s == NULL) || (n == 0))    /* nothing to stuff? */
  71.     return(CMxOK);        /* ok by me! */
  72.  
  73.   if (cmcsb._cmflg & CM_NEC)
  74.     flags |= CC_NEC;        /* set noecho flag if source not echoable */
  75.  
  76.   if (flags & CC_HID)
  77.     flags |= CC_NEC;        /* hidden implies not echoed */
  78.  
  79.   if (flags & CC_ACT)
  80.     flags |= CC_ACT;
  81.   cmcsb._cmflg |= CM_DRT;    /* buffer is now dirty */    
  82.  
  83.   while (n-- != 0) {        /* loop through to end of string */
  84.     if ((n < 0) && (*s == NULCHAR)) /* check for null termination */
  85.       return(CMxOK);
  86.     if (cmcsb._cmcnt == 0)    /* watch for buffer overflow */
  87.       return(CMxBOVF);
  88.     c = *s++;            /* get next char to stuff */
  89.     if ((flags & CC_NEC) == 0)
  90.       cmechx(c);        /* echo chars if needed */
  91.     if ((cmcsb._cmflg & CM_RAI) && /* raising input? */
  92.     ((flags & CC_QUO) == 0) /* and not quoting this char? */
  93.        )
  94.       if ((c >= 'a') && (c <= 'z')) /* and this char is lowercase? */
  95.     c -= 'a'-'A';        /* yup, convert to uppercase */
  96.     *(cmcsb._cmptr + cmcsb._cminc++) = c | flags; /* deposit char w/ flags */
  97.     cmcsb._cmcnt--;        /* and adjust space remaining */
  98.   }
  99.   return(CMxOK);        /* all done */
  100. }
  101.  
  102. int
  103. cmsti1(c,flags)
  104. char c;
  105. int flags;
  106. {
  107.   return(cmstin(&c,1,flags)); /* stuff one character */
  108. }
  109.  
  110.  
  111. int
  112. cmsti(s,flags)
  113. char *s;
  114. int flags;
  115. {
  116.   return(cmstin(s,-1,flags));    /* -1 means stuff til null char */
  117. }
  118.  
  119.  
  120.  
  121. /* echo
  122. **
  123. ** Purpose:
  124. **   Print character representations on the source terminal.  Control
  125. **   characters are printed as in ^A for control-A.  Delete prints as
  126. **   ^?.  Linefeed echoes as a newline sequence.  Tabs are expanded
  127. **   into equivalent spaces.  If the column position exceeds _cmcmx in the
  128. **   CSB a newline is generated before echoing the character.
  129. **
  130. ** Input arguments:
  131. **   c - The character to be echoed.
  132. **
  133. ** Output arguments: None
  134. ** Returns: Nothing.
  135. **/
  136.  
  137. cmechx(c)
  138. char c;
  139. {
  140.   int cpos;            /* column counter */
  141.  
  142.   c &= 0x7f;
  143.  
  144.   if (c == NEWLINE)        /* newline */
  145.     cmxnl();
  146.   else if (c == TAB)        /* tab */
  147.     do {
  148.       cmechx(SPACE);
  149.     } while ((cmcsb._cmcol != 0) && ((cmcsb._cmcol % 8) != 0));
  150.                     /* space to newline or a multiple of 8 */
  151.   else if ((c == DELETE) || (c < SPACE && c != 033)){ /* other control char? */
  152.     cmechx('^');        /* print up-arrow */
  153.     cmechx((char) (c ^ 0x40));
  154.   }
  155.   else {            /* normal character */
  156.     cmxputc(c);            /* just print it */
  157.   }
  158.   /*
  159.    * XXX This wraps one column too early.  This sidesteps the "am && !xn"
  160.    * problem, but it's not consistent with other code (that does step into
  161.    * the problem).  Should we always avoid the last column?  Otherwise
  162.    * cmnl() should look at the _cmcol, am, and xn.  -- chris
  163.    */
  164.   if ((cmcsb._cmcol >= cmcsb._cmcmx) && (c != NEWLINE))
  165.     cmxnl();
  166. }
  167.  
  168.  
  169.  
  170. /* cmhelp
  171. **
  172. ** Purpose:
  173. **   Steps through a chain of FDB's and prints a help message for
  174. **   each one.  If the user supplied a help string, it is output
  175. **   first.  Then, if the CM_SDH flag is off in the FDB, the function's
  176. **   help routine is invoked to print the standard help message for
  177. **   the field.  A flag is passed to the standard help routine indicating
  178. **   whether or not a custom help string was printed.
  179. **
  180. ** Input arguments:
  181. **   fdblist - A pointer to the first FDB in the chain of alternates.
  182. **   helpchar - The character that invoked the help action (normally '?'),
  183. **     to be echoed before the help message.  Pass NULCHAR ('\0') to
  184. **     suppress this.
  185. **
  186. ** Output arguments: None.
  187. ** Returns: Standard return code.
  188. **/
  189.  
  190. int
  191. cmhelp(fdblist,helpchar)
  192. fdb *fdblist;
  193. char helpchar;
  194. {
  195.   int firsthelp = TRUE;        /* guides whether to print "or" */
  196.   int cust;            /* TRUE if custom help given for an FDB */
  197.   int ret;            /* return code from handlers */
  198.   int inputlen;            /* count of input available */
  199.   ftspec *ft;            /* function handler for fdb */
  200.   int *cp, *cpmax;        /* for scanning buffer during refresh */
  201. #ifdef undef
  202.   if ((cmcsb._cmflg & CM_TTY) == 0)
  203.     return(CMxOK);        /* no help to non terminals */
  204. #endif
  205.  
  206.                 /* remove hyphen-newline combinations */
  207.   ret = cmprep(cmcsb._cmwbp,cmcsb._cmwbc,&inputlen);
  208.   if (ret != CMxOK)
  209.     return(ret);        /* propagate errors */
  210.   if (helpchar != NULCHAR) {
  211.     cmechx(helpchar);        /* this is generally a question mark */
  212.     cmechx(SPACE);        /* and separate from help text */
  213.   }
  214.   while (fdblist != NULL) {
  215.     cust = (fdblist->_cmhlp != NULL); /* see if they gave a help string */
  216.     if (cust || ((fdblist->_cmffl & CM_SDH) == 0)) /* any help at all? */
  217.       if (firsthelp)
  218.     firsthelp = FALSE;    /* yes, future FDB's won't be first msg */
  219.       else
  220.     cmxputs("  or ");    /* start alternative when not the first */
  221.     if (cust)
  222.       cmxputs(fdblist->_cmhlp);    /* print custom help if any */
  223.     if ((fdblist->_cmffl & CM_SDH) == 0) { /* std help if not suppressed */
  224.       ft = cmfntb[fdblist->_cmfnc-1]; /* get the function handler */
  225.                       /* and invoke the help handler */
  226.       ret = (*ft->_fthlp)(cmcsb._cmwbp,inputlen,fdblist,cust); 
  227.     }
  228.     if (ret != CMxOK)
  229.       return(ret);        /* propagate errors immediately */
  230.     if (cust || ((fdblist->_cmffl & CM_SDH) == 0))
  231.       cmxnl();            /* if any help given, finish with newline */
  232.     fdblist = fdblist->_cmlst;    /* now move on to next choice */
  233.   }
  234.   cmxputs(cmcsb._cmrty);    /* now reprint prompt */
  235.   cpmax = cmcsb._cmptr + cmcsb._cminc; /* this is as far as refresh goes */
  236.   for (cp = cmcsb._cmbfp; cp != cpmax; cp++) /* loop through buffered input */
  237.     if ((*cp & CC_NEC) == 0)    /* originally echoed? */
  238.       cmechx((char) *cp & CC_CHR); /* yup, echo it again */
  239.   return(CMxOK);        /* Fine! */
  240. }
  241.  
  242.  
  243.  
  244. /* cmcplt
  245. **
  246. ** Purpose:
  247. **   Attempt to get completion for the current parse field.  Field
  248. **   _cmifd of the CSB must be pointing to an FDB which produced an
  249. **   incomplete parse on the current input.  The completion handler
  250. **   for that FDB is invoked to provide completion text, which is
  251. **   stuffed into the command buffer with echoing.  Either CMxOK
  252. **   or CMxGO is returned, depending on whether or not the completion
  253. **   handler requested wakeup.  Any completion that asks for wakeup
  254. **   causes flag CM_PFE to be turned on in the CSB, to activate following
  255. **   noise word fields.
  256. **
  257. ** Input arguments:
  258. **   full - If TRUE, full completion will be requested.  Otherwise,
  259. **     partial completion will be requested.
  260. **
  261. ** Output arguments: None.
  262. ** Returns: CMxGO for wakeup, or CMxOK for no wakeup.
  263. **/
  264.  
  265. int
  266. cmcplt(full)
  267. int full;
  268. {
  269.   int ret;            /* return code from aux routines */
  270.   int flags;            /* flags returned by completion handler */
  271.   int (*cmp)();            /* function completion handler */
  272.   int inputlen;            /* input count available */
  273.   char *ctext;            /* pointer to returned completion text */
  274.   int ctlen;            /* number of characters in completion text */
  275.   int i;
  276.   
  277.   ret = cmprep(cmcsb._cmwbp,cmcsb._cmwbc,&inputlen); /* clean up input */
  278.   if (ret != CMxOK)
  279.     return(ret);        /* propagate errors */
  280.  
  281.   cmp = cmfntb[cmcsb._cmifd->_cmfnc-1]->_ftcmp; /* get the handler */
  282.                 /* and invoke it */
  283.   flags = (*cmp)(cmcsb._cmwbp,inputlen,cmcsb._cmifd,full,&ctext,&ctlen);
  284.   if (ctext != NULL)
  285.     if (flags & CMP_PNC) {     /* stop after punctuation? */
  286.       for (i = 0; (i < ctlen) || (ctlen == -1); i++) /* scan text */
  287.         if ((ctlen == -1) && (ctext[i] == NULCHAR))
  288.         break;        /* stop at end of null-terminated string */
  289.         else if (!isalnum(ctext[i])) {    /* XXX */
  290.       i++;            /* count a punctuation character */
  291.       break;        /* and stop scanning */
  292.         }
  293.       ctlen = i;        /* only stuff this much */
  294.     }
  295.   ret = cmstin(ctext,ctlen,0);    /* stuff the supplied completion text */
  296.   cmxflsh();
  297.   if (ret != CMxOK)
  298.     return(ret);        /* propagate problem */
  299.   if (flags & CMP_SPC) {
  300.     ret = cmsti1(SPACE,0);    /* and trailing space if requested */
  301.     cmxflsh();
  302.     if (ret != CMxOK)
  303.       return(ret);        /* propagate problem */
  304.   }
  305.   if (flags & CMP_BEL) {
  306.     cmputc(BELL,cmcsb._cmoj);        /* beep if they asked us to */
  307.     cmxflsh();
  308.   }
  309.   if (flags & CMP_GO) {
  310.     cmcsb._cmflg |= CM_PFE;    /* wants wakeup - activate noise words */
  311.     return(CMxGO);        /* return success with wakeup */
  312.   }
  313.   else
  314.     return(CMxOK);        /* or without */
  315. }
  316.  
  317.  
  318.  
  319. /* cmdflt
  320. **
  321. ** Purpose:
  322. **   Fill in a default string if appropriate.  If the command buffer
  323. **   is empty, and if any of the FDB's for the current parse specifies
  324. **   a default string, it is stuffed into the buffer and echoed.
  325. **
  326. ** Input arguments:
  327. **   fdblist - A pointer to the chain of FDB's.
  328. **
  329. ** Output arguments: None.
  330. ** Returns: CMxOK if default successfully stuffed, other standard return
  331. **   code otherwise.
  332. **/
  333.  
  334. int
  335. cmdflt(fdblist)
  336. fdb *fdblist;
  337. {
  338.   char *dflt;                /* string to stuff */
  339.   int ret;
  340.  
  341.   if (cmcsb._cminc == 0) {
  342.     while (fdblist != NULL)        /* step through FDB's */
  343.       if ((dflt = fdblist->_cmdef) != NULL)
  344.     break;                /* until a default is found */
  345.       else
  346.     fdblist = fdblist->_cmlst;    /* move to next FDB */
  347.     if (dflt != NULL) {
  348.       ret = cmsti(dflt,0);        /* then stuff it with echoing */
  349.       if (ret == CMxOK)
  350.     ret == cmsti1(SPACE,0);        /* followed by a space */
  351.       return(ret);
  352.     }
  353.   }
  354.   return(CMxNDEF);            /* nothing stuffed */
  355. }
  356.  
  357. /*
  358.  * CMPDFLT:
  359.  * fill in default for partial matches
  360.  * only stuff the string up to a punctuation...
  361.  */
  362.  
  363. int
  364. cmpdflt(fdblist)
  365. fdb *fdblist;
  366. {
  367.   char *dflt;                /* string to stuff */
  368.   int ret;
  369.   int i;
  370.  
  371.   if (cmcsb._cminc == 0) {
  372.     while (fdblist != NULL)        /* step through FDB's */
  373.       if ((dflt = fdblist->_cmdef) != NULL)
  374.     break;                /* until a default is found */
  375.       else
  376.     fdblist = fdblist->_cmlst;    /* move to next FDB */
  377.     if (dflt != NULL) {
  378.       for( i = 0; i < strlen(dflt) ; i++) {
  379.     if (!isalnum(dflt[i])) break;    /* XXX */
  380.     ret = cmsti1(dflt[i],0);
  381.     if (ret != CMxOK)
  382.       break;
  383.       }
  384.       return(ret);
  385.     }
  386.   }
  387.   return(CMxNDEF);            /* nothing stuffed */
  388. }
  389.  
  390.  
  391. /* cmprep
  392. **
  393. ** Purpose:
  394. **   Copy unparsed input text with skipped characters removed
  395. **   into another buffer, without the high order flag bytes.
  396. **   As a side effect, any ineligible conditional skip characters
  397. **   have their CC_CSK flags cleared.
  398. **
  399. ** Input arguments:
  400. **   tobuf - Pointer to buffer to hold prepared text.
  401. **   tosize - Size of destination buffer.
  402. **
  403. ** Output arguments:
  404. **   tolen - Number of characters in prepared text.
  405. **
  406. ** Returns: Standard return code.
  407. **/
  408.  
  409. int
  410. cmprep(tobuf,tosize,tolen)
  411. char *tobuf;
  412. int tosize, *tolen;
  413. {
  414.   char c;            /* individual chars from destination */
  415.   int cc;
  416.   int *frombuf;            /* pointer to source buffer */
  417.   int fromlen;            /* number of chars to copy from source */
  418.   int cskip = 0;        /* number of conditional skips in a run */
  419.   int *cskippos;        /* position of start of run */
  420.  
  421.   frombuf = cmcsb._cmptr;    /* point to text to be copied */
  422.   fromlen = cmcsb._cminc;    /* number of chars to copy */
  423.   *tolen = 0;            /* no chars copied yet */
  424.   while (fromlen-- > 0)    {    /* loop over source */
  425.     c = (cc = *frombuf++) & CC_QCH; /* get next source character */
  426.     if (cc & CC_SKP) {        /* skip this character? */
  427.       cskip = 0;        /* yes, and last run of conditional skips */
  428.     }
  429.     else if (cc & CC_CSK) {    /* conditionally skip character? */
  430.       if (cskip++ == 0)        /* count and check for start of run */
  431.     cskippos = frombuf-1;    /* start of run -- remember position */
  432.     }
  433.     else {            /* no type of skip */
  434.       while (cskip-- > 0) {    /* maybe we ended a run of ineligible skips */
  435.         if (*tolen >= tosize)    /* copy each char */
  436.           return(CMxIOVF);    /* no room */
  437.     *tobuf++ = *cskippos & CC_QCH;
  438.     (*tolen)++;        /* and count it */
  439.     *cskippos++ &= ~CC_CSK; /* and turn off conditional skip flag */
  440.       }
  441.       cskip = 0;        /* (above loop leaves cskip == -1) */
  442.       if (*tolen >= tosize)    /* room for current char? */
  443.     return(CMxIOVF);    /* nope */
  444.       *tobuf++ = c;        /* copy it */
  445.       (*tolen)++;        /* and count it */
  446.     }
  447.   }
  448.   while (cskip-- > 0) {        /* in case we finished with a run */
  449.     if (*tolen >= tosize)    /* copy each char */
  450.       return(CMxIOVF);        /* no room */
  451.     *tobuf++ = *cskippos & CC_CHR;
  452.     (*tolen)++;            /* and count it */
  453.     *cskippos++ &= ~CC_CSK;    /* and turn off conditional skip flag */
  454.   }
  455.   return(CMxOK);
  456. }
  457.  
  458.  
  459.  
  460. /* cmperr, cmgerr
  461. **
  462. ** Purpose:
  463. **   Print or return a pointer to an error message corresponding to a
  464. **   given ccmd return code.  Cmperr prints the message, starting at
  465. **   the left edge of a new line, and prefixed with a question mark.
  466. **   Cmgerr returns a pointer to the message string, without a question
  467. **   mark.  In addition, cmperr flushes any typeahead that has accumulated
  468. **   at the input source.
  469. **
  470. ** Input arguments:
  471. **   ecode - The return code to be interpreted.
  472. **
  473. ** Output arguments: None.
  474. ** Returns: Nothing.
  475. **/
  476.  
  477. char *
  478. cmgerr(ecode)
  479. int ecode;
  480. {
  481.   int lh,rh;            /* pieces of decomposed code */
  482.   static char unkerr[] =    /* template for return string for bad code */
  483.     "Unknown command parsing error: xxx, xxx ";
  484.  
  485.   lh = ecode >> 8;        /* decompose error code */
  486.   rh = ecode & 0xff;
  487.   
  488.   if ((lh > cmfmax) ||        /* function code out of range? */
  489.       (rh >= fnetab[lh]->_fecnt) /* or error code too high for function? */
  490.      ) {
  491.     sprintf(unkerr,"Unknown command parsing error: %3d, %3d",lh,rh);
  492.     return(unkerr);        /* return catch-all message */
  493.   }
  494.   else
  495.     return(fnetab[lh]->_ferrs[rh]); /* else return selected message */
  496. }
  497.  
  498. cmperr(ecode)
  499. int ecode;
  500. {
  501.   char *estr;
  502.  
  503. #ifdef undef
  504.   if ((cmcsb._cmflg & CM_TTY) == 0)
  505.     return;            /* no output to nonterminal */
  506. #endif
  507.   estr = cmgerr(ecode);        /* get the error code */
  508.   cmflush(cmcsb._cmij);        /* flush waiting input */
  509.   if (cmcpos() != 0)
  510.     cmnl(cmcsb._cmej);        /* get to beginning of line */
  511.   cmcsb._cmcol = 0;        /* make sure our counter agrees */
  512.   cmputc('?',cmcsb._cmej);    /* start with question mark */
  513.   cmputs(estr,cmcsb._cmej);    /* then the error string */
  514.   if (cmcsb._cmcnt > 0) {
  515.     int i = 0;
  516.     int empty = TRUE;        /* may not be anything to print */
  517.     char c = cmcsb._cmptr[0];
  518.     while (i < cmcsb._cminc && isascii(c) && isprint(c)) {
  519.       if (empty)        /* found something, print separator */
  520.     cmputs(" - \"", cmcsb._cmej), empty = FALSE;
  521.       cmputc(c, cmcsb._cmej);
  522.       c = cmcsb._cmptr[++i];
  523.     }
  524.     if (!empty)
  525.       cmputc('"', cmcsb._cmej);
  526.   }      
  527.   cmnl(cmcsb._cmej);        /* tie off with newline */
  528. }
  529.  
  530.  
  531.  
  532. /* fdbchn
  533. **
  534. ** Purpose:
  535. **   Chain together a list of FDB's, and return a pointer to
  536. **   the first FDB in the chain.
  537. **
  538. ** Input arguments:
  539. **   fdbs - A comma-separated list of pointers to FDB's to be 
  540. **     chained together, terminated by a NULL pointer.
  541. **
  542. ** Output arguments: None.
  543. ** Returns: Pointer to the head of the FDB chain.
  544. **/
  545.  
  546. fdb *
  547. fdbchn(va_alist)
  548. va_dcl
  549. {
  550.   va_list fdbs;
  551.   fdb *head, *fdbptr;
  552.  
  553.   va_start(fdbs);
  554.  
  555.   head = fdbptr = (fdb *) va_arg(fdbs, fdb *);
  556.  
  557.   while(fdbptr)
  558.     fdbptr = fdbptr->_cmlst = (fdb *) va_arg(fdbs, fdb *);
  559.  
  560.   va_end(fdbs);
  561.  
  562.   return(head);            /* all linked */
  563. }
  564.  
  565.